package com.gt.wxpay.cashier.payChannel.handler;

import com.gt.wxpay.cashier.domain.dto.QueryRefundDto;
import com.gt.wxpay.cashier.domain.model.CashierPayment;
import com.gt.wxpay.cashier.payChannel.application.WxConfigService;
import com.gt.wxpay.cashier.payChannel.callback.vo.WxPayRespDto;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.*;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class NativePrepayHandler {
    @Autowired
    private WxConfigService wxConfigService;

    @SuppressWarnings("all")
    public WxPayRespDto handlePrepay(CashierPayment cashierPayment) {
        // 微信支付渠道配置
        String payChannelId = cashierPayment.getPayChannelId();
        Config wxConfig = wxConfigService.getWxConfig(Long.parseLong(payChannelId));
        String wxCallBackUrl = wxConfigService.getWxCallBackUrl(false);
        PrepayRequest request = new PrepayRequest();
        // 微信appId
        request.setAppid(cashierPayment.getAppId());
        // 微信商户id||是微信支付收款的商户ID--来自渠道{不是业务商户}
        request.setMchid(wxConfig.createCredential().getMerchantId());
        // 商品描述
        request.setDescription(cashierPayment.getDescription());
        // 回调地址，仅支持https||微信侧回调收银台的地址
        request.setNotifyUrl(wxCallBackUrl);
        // 支付订单号||收银台侧的流水号||不是业务系统的流水号
        request.setOutTradeNo(cashierPayment.getPaymentNo());
        // 订单金额
        Amount amount = new Amount();
        amount.setTotal(cashierPayment.getAmount().intValue());
        amount.setCurrency(StringUtils.isEmpty(cashierPayment.getCurrencyType()) ? "CNY" : cashierPayment.getCurrencyType());
        request.setAmount(amount);
        // 支付环境
        if (StringUtils.isNotBlank(cashierPayment.getClientIp())) {
            SceneInfo sceneInfo = new SceneInfo();
            sceneInfo.setPayerClientIp(cashierPayment.getClientIp());
            request.setSceneInfo(sceneInfo);
        }
        // 发起下单
        NativePayService service = new NativePayService.Builder().config(wxConfig).build();
        PrepayResponse prepay = service.prepay(request);
        if (prepay == null || StringUtils.isEmpty(prepay.getCodeUrl())) {
            log.error("发起支付失败, 返回的二维码链接为空");
            throw new RuntimeException("发起支付失败, 返回的二维码链接为空");
        }
        // 封装返回数据
        return this.getWxPayRespVo(prepay.getCodeUrl());
    }

    public WxPayRespDto getWxPayRespVo(String codeUrl) {
        return new WxPayRespDto()
                .setCodeUrl(codeUrl);
    }

    /**
     * 商户可以通过查询订单接口主动查询订单状态，完成下一步的业务逻辑。
     * <p>
     * 查询订单可通过微信支付订单号 (opens new window)和商户订单号 (opens new window)两种方式查询。
     * <p>
     * 需要调用查询接口的情况：
     * <p>
     * 当商户后台、网络、服务器等出现异常，商户系统最终未接收到支付通知。
     * 调用支付接口后，返回系统错误或未知交易状态情况。
     * 调用付款码支付API，返回USERPAYING的状态。
     * 调用关单或撤销接口API之前，需确认支付状态。
     * #接口说明
     *
     * @param cashierPayment cashierPayment
     * @return WxPayRespDto
     */
    public Transaction queryOrderInfo(CashierPayment cashierPayment) {
        String payChannelId = cashierPayment.getPayChannelId();
        Config wxConfig = wxConfigService.getWxConfig(Long.parseLong(payChannelId));
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(wxConfig.createCredential().getMerchantId());
        request.setOutTradeNo(cashierPayment.getPaymentNo());
        NativePayService service =
                new NativePayService.Builder()
                        .config(wxConfig)
                        .build();
        Transaction transaction = null;
        try {
            transaction = service.queryOrderByOutTradeNo(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return transaction;
    }

    /**
     * 关闭订单，以下情况需要调用关单接口：
     * <p>
     * 商户订单支付失败需要生成新单号重新发起支付，要对原订单号调用关单，避免重复支付；
     * 系统下单后，用户支付超时，系统退出不再受理，避免用户继续，请调用关单接口。
     *
     * @param cashierPayment cashierPayment
     */
    public void closePayment(CashierPayment cashierPayment) {
        CloseOrderRequest request = new CloseOrderRequest();
        Config wxConfig = wxConfigService.getWxConfig(Long.parseLong(cashierPayment.getPayChannelId()));
        request.setMchid(cashierPayment.getMerchantId());
        // 商户系统内部订单号
        request.setOutTradeNo(cashierPayment.getPaymentNo());
        NativePayService service = new NativePayService.Builder().config(wxConfig).build();
        service.closeOrder(request);
    }

    public Refund refundOrderV3(CashierPayment cashierPayment) {
        Config wxConfig = wxConfigService.getWxConfig(Long.parseLong(cashierPayment.getPayChannelId()));
        RefundService service = new RefundService.Builder().config(wxConfig).build();
        CreateRequest request = new CreateRequest();
        // 退款收银台订单号
        request.setOutRefundNo(cashierPayment.getPaymentNo());
        // 微信支付订单号(原支付渠道流水号)
        request.setTransactionId(cashierPayment.getChannelFlowNo());
        // 使用可用余额退款 ===== request.setFundsAccount(ReqFundsAccount.AVAILABLE);
        request.setNotifyUrl(cashierPayment.getNotifyUrl());
        // 退款金额
        AmountReq amountReq = new AmountReq();
        amountReq.setTotal(cashierPayment.getAmount());
        amountReq.setRefund(cashierPayment.getRefundAmount());
        amountReq.setCurrency(StringUtils.isEmpty(cashierPayment.getCurrencyType()) ? "CNY" : cashierPayment.getCurrencyType());
        request.setAmount(amountReq);
        // 发起退款
        Refund refund = service.create(request);
        if (refund.getStatus() == Status.SUCCESS ||
                refund.getStatus() == Status.PROCESSING) {
            log.info("发起退款成功");
            return refund;
        } else {
            log.info("发起退款失败：{}", refund.getStatus());
            throw new RuntimeException("发起退款失败");
        }
    }

    public QueryRefundDto queryRefundOrderV3(CashierPayment refundOrder) {
        return null;
    }
}
